// clscacheModel.go
package models

import (
	//"errors"
	//"fmt"

	"fmt"
	"log"

	"bytes"
	"encoding/json"
	"io/ioutil"
	"strings"

	"time"

	//"strconv"

	"github.com/beego/beego/v2/client/orm"
	//"github.com/beego/beego/v2/core/validation"
)

//lua读取的配置
type Xf_waf_config struct {
	Id                   int64  `json:"id"`
	Appname              string `json:"appname"`
	Waf_enable           string `json:"waf_enable"`
	Log_model            string `json:"log_model"`
	Kafka_broker_ip      string `json:"kafka_broker_ip"`
	Waf_exclude_intranet bool   `json:"waf_exclude_intranet"`
	Waf_exclude_static   bool   `json:"waf_exclude_static"`
	White_url_check      bool   `json:"white_url_check"`
	White_ip_check       bool   `json:"white_ip_check"`
	White_ua_check       bool   `json:"white_ua_check"`
	White_rf_check       bool   `json:"white_rf_check"`
	Black_ip_check       bool   `json:"black_ip_check"`
	Url_check            bool   `json:"url_check"`
	Url_args_check       bool   `json:"url_args_check"`
	User_agent_check     bool   `json:"user_agent_check"`
	Referer_check        bool   `json:"referer_check"`
	Cookie_check         bool   `json:"cookie_check"`
	Post_check           bool   `json:"post_check"`
	Waf_model            string `json:"waf_model"`
	Waf_html             string `json:"waf_html"`
	Waf_redirect_url     string `json:"waf_redirect_url"`
	Waf_expire_time      int64  `json:"waf_expire_time"`
	Waf_url_length       int64  `json:"waf_url_length"`

	Risk_ip_check     bool              `json:"risk_ip_check"`
	Waf_riskip_model  string            `json:"waf_riskip_model"`
	Waf_riskip_ratev  float64           `json:"waf_riskip_ratev"`
	Waf_urldeny_check bool              `json:"waf_urldeny_check"`
	Urldeny_rule      []*Xf_waf_urldeny `json:"urldeny_rule" orm:"reverse(many)"`

	Reqlimit_enable                  string `json:"reqlimit_enable"`
	Reqlimit_exclude_static          bool   `json:"reqlimit_exclude_static"`
	Reqlimit_exclude_intranet        bool   `json:"reqlimit_exclude_intranet"`
	Reqlimit_exclude_cookie          bool   `json:"reqlimit_exclude_cookie"`
	Exclude_cookie                   string `json:"exclude_cookie"`
	Reqlimit_exclude_subrequest      bool   `json:"reqlimit_exclude_subrequest"`
	Reqlimit_exclude_internalrequest bool   `json:"reqlimit_exclude_internalrequest"`
	Reqlimit_white_enable            bool   `json:"reqlimit_white_enable"`
	Reqlimit_statuscode              int    `json:"reqlimit_statuscode"`
	Limit_html                       string `json:"limit_html"`

	Reqlimit_rule      []*Xf_reqlimit_rule       `json:"reqlimit_rule" orm:"reverse(many)"`
	Reqlimit_whitelist []*Xf_reqlimit_white_list `json:"reqlimit_whitelist" orm:"reverse(many)"`
	Updatetime         string                    `json:"updatetime"`
}

//web安全页配置
type Wafconfig struct {
	Id                   int64  `json:"id"`
	Appname              string `json:"appname"`
	Waf_enable           string `json:"waf_enable"`
	Waf_exclude_intranet bool   `json:"waf_exclude_intranet"`
	Waf_exclude_static   bool   `json:"waf_exclude_static"`
	//Waf_exclude_pcsuv    bool   `json:"waf_exclude_pcsuv"`
	White_url_check   bool `json:"white_url_check"`
	White_ip_check    bool `json:"white_ip_check"`
	White_ua_check    bool `json:"white_ua_check"`
	White_rf_check    bool `json:"white_rf_check"`
	Black_ip_check    bool `json:"black_ip_check"`
	Url_check         bool `json:"url_check"`
	Url_args_check    bool `json:"url_args_check"`
	User_agent_check  bool `json:"user_agent_check"`
	Referer_check     bool `json:"referer_check"`
	Cookie_check      bool `json:"cookie_check"`
	Post_check        bool `json:"post_check"`
	Risk_ip_check     bool `json:"risk_ip_check"`
	Waf_urldeny_check bool `json:"waf_urldeny_check"`
	//Waf_model            string `json:"waf_model"`
	//Waf_html             string `json:"waf_html"`
	//Waf_redirect_url     string `json:"waf_redirect_url"`
	//Waf_expire_time      int64  `json:"waf_expire_time"`
	//Waf_url_length       int64  `json:"waf_url_length"`
	Updatetime string `json:"updatetime"`
}

//防刷限流页读取的配置
type Reqlimitconfig struct {
	Id                               int64  `json:"id"`
	Appname                          string `json:"appname"`
	Reqlimit_enable                  string `json:"reqlimit_enable"`
	Reqlimit_exclude_static          bool   `json:"reqlimit_exclude_static"`
	Reqlimit_exclude_intranet        bool   `json:"reqlimit_exclude_intranet"`
	Reqlimit_exclude_cookie          bool   `json:"reqlimit_exclude_cookie"`
	Reqlimit_exclude_subrequest      bool   `json:"reqlimit_exclude_subrequest"`
	Reqlimit_exclude_internalrequest bool   `json:"reqlimit_exclude_internalrequest"`
	Reqlimit_white_enable            bool   `json:"reqlimit_white_enable"`
	//Reqlimit_statuscode       int    `json:"reqlimit_statuscode"`
	//Limit_html                string `json:"limit_html"`
	Updatetime string `json:"updatetime"`
}

//其他配置项
type Otherconfig struct {
	Id                  int64   `json:"id"`
	Appname             string  `json:"appname"`
	Kafka_broker_ip     string  `json:"kafka_broker_ip"`
	Log_model           string  `json:"log_model"`
	Waf_model           string  `json:"waf_model"`
	Waf_html            string  `json:"waf_html"`
	Waf_redirect_url    string  `json:"waf_redirect_url"`
	Waf_expire_time     int64   `json:"waf_expire_time"`
	Waf_url_length      int64   `json:"waf_url_length"`
	Waf_riskip_model    string  `json:"waf_riskip_model"`
	Waf_riskip_ratev    float64 `json:"waf_riskip_ratev"`
	Reqlimit_statuscode int     `json:"reqlimit_statuscode"`
	Limit_html          string  `json:"limit_html"`
	Exclude_cookie      string  `json:"exclude_cookie"`
	Updatetime          string  `json:"updatetime"`
}

type Configversion struct {
	Updatetime string `json:"updatetime"`
}

type Xf_reqlimit_rule struct {
	Id             int64          `json:"-"`
	Appname        string         `json:"-" valid:"Required"`
	Limit_host     string         `json:"limit_host"`
	Limit_url      string         `json:"limit_url"`
	Limit_type     string         `json:"limit_type"`
	Limit_key      string         `json:"limit_key"`
	Limit_rate     float64        `json:"limit_rate"`
	Limit_speed    string         `json:"limit_speed"`
	Limit_enable   string         `json:"limit_enable"`
	Limit_downrate int64          `json:"limit_downrate"`
	Xf_waf_config  *Xf_waf_config `json:"-" orm:"rel(fk)"`
}

type Xf_reqlimit_white_list struct {
	Id            int64          `json:"-"`
	Appname       string         `json:"-" valid:"Required"`
	Ruletype      string         `json:"ruletype" valid:"Required"`
	Ruleitem      string         `json:"ruleitem" valid:"Required"`
	Enable        bool           `json:"enable"`
	Xf_waf_config *Xf_waf_config `json:"-" orm:"rel(fk)"`
}

type Xf_waf_urldeny struct {
	Id            int64          `json:"-"`
	Appname       string         `json:"-" valid:"Required"`
	Deny_host     string         `json:"deny_host"`
	Deny_url      string         `json:"deny_url"`
	Deny_type     string         `json:"deny_type"`
	Deny_key      string         `json:"deny_key"`
	Deny_enable   string         `json:"deny_enable"`
	Xf_waf_config *Xf_waf_config `json:"-" orm:"rel(fk)"`
}

func init() {
	orm.Debug = true
	orm.RegisterModel(new(Xf_waf_config), new(Xf_reqlimit_rule), new(Xf_reqlimit_white_list), new(Xf_waf_urldeny))
}

//lua获取的配置
func GetConfigAppname(appname string) Xf_waf_config {
	o := orm.NewOrm()
	var stu Xf_waf_config
	_ = o.Raw("select * from xf_waf_config where appname=?", appname).QueryRow(&stu)

	var xrr []*Xf_reqlimit_rule
	_, err := o.Raw("select * from xf_reqlimit_rule where appname=? AND limit_enable = 'true' ORDER BY limit_priority,limit_host DESC,limit_url DESC,CONVERT(limit_rate,signed)", appname).QueryRows(&xrr)
	if err != nil {
		log.Println(err)
	}
	stu.Reqlimit_rule = xrr

	var xrwl []*Xf_reqlimit_white_list
	//_, err2 := o.Raw("select * from xf_reqlimit_white_list where appname=? AND enable = 'true' ORDER BY id DESC", appname).QueryRows(&xrwl)
	//加上enable=true时会导致禁用规则后，如果全部规则为禁用时，是null值。这样不会改变内存中的值。
	_, err2 := o.Raw("select * from xf_reqlimit_white_list where appname=? ORDER BY id DESC", appname).QueryRows(&xrwl)
	if err2 != nil {
		log.Println(err2)
	}
	stu.Reqlimit_whitelist = xrwl

	//每个应用给一条禁用的默认值，要不读取后会变成null
	var xwd []*Xf_waf_urldeny
	_, err3 := o.Raw("select * from xf_waf_urldeny where appname=? ORDER BY id", appname).QueryRows(&xwd)
	if err3 != nil {
		log.Println(err3)
	}
	stu.Urldeny_rule = xwd

	return stu
}

//web安全配置页获取的配置
func GetWafConfigByAppname(appname string) Wafconfig {
	o := orm.NewOrm()
	var stu Wafconfig
	//_ = o.Raw("select id,appname,waf_enable,log_model,log_dir,waf_exclude_intranet,waf_exclude_static,white_url_check,white_ip_check,white_ua_check,black_ip_check,url_check,url_args_check,user_agent_check,cookie_check,post_check,waf_model,waf_html,waf_redirect_url,waf_expire_time,waf_url_length,updatetime from xf_waf_config where appname=?", appname).QueryRow(&stu)
	_ = o.Raw("select id,appname,waf_enable,waf_exclude_intranet,waf_exclude_static,white_url_check,white_ip_check,white_ua_check,white_rf_check,black_ip_check,waf_urldeny_check,url_check,url_args_check,user_agent_check,referer_check,cookie_check,post_check,risk_ip_check,updatetime from xf_waf_config where appname=?", appname).QueryRow(&stu)
	return stu
}

//lua获取配置版本
func GetConfigVersionByAppname(appname string) int64 {
	o := orm.NewOrm()
	var stu Configversion
	_ = o.Raw("select updatetime from xf_waf_config where appname=?", appname).QueryRow(&stu)
	loc, _ := time.LoadLocation("Local")
	//log.Println(stu.Updatetime)
	tm1, _ := time.ParseInLocation("2006-01-02 15:04:05", stu.Updatetime, loc)
	return tm1.Unix()
}

//防刷限流配置页获取配置
func GetReqLimitConfigByAppname(appname string) Reqlimitconfig {
	o := orm.NewOrm()
	var stu Reqlimitconfig
	//_ = o.Raw("select id,appname,reqlimit_enable,reqlimit_exclude_static,reqlimit_exclude_intranet,reqlimit_exclude_pcsuv,reqlimit_white_enable,reqlimit_statuscode,limit_html,updatetime from xf_waf_config where appname=?", appname).QueryRow(&stu)
	_ = o.Raw("select id,appname,reqlimit_enable,reqlimit_exclude_static,reqlimit_exclude_intranet,reqlimit_exclude_cookie,reqlimit_exclude_subrequest,reqlimit_exclude_internalrequest,reqlimit_white_enable,updatetime from xf_waf_config where appname=?", appname).QueryRow(&stu)
	return stu
}

//其他配置项
func GetOtherConfigByAppname(appname string) Otherconfig {
	o := orm.NewOrm()
	var stu Otherconfig
	_ = o.Raw("select id,appname,kafka_broker_ip,log_model,waf_model,waf_html,waf_redirect_url,waf_expire_time,waf_url_length,waf_riskip_model,waf_riskip_ratev,reqlimit_statuscode,limit_html,exclude_cookie,updatetime from xf_waf_config where appname=?", appname).QueryRow(&stu)
	return stu
}

func EditWafConfigByAppname(appname, key, value string) (int64, error) {
	o := orm.NewOrm()
	sqlstr := fmt.Sprintf("UPDATE xf_waf_config SET %s = '%s' WHERE appname = '%s'", key, value, appname)
	//res, err := o.Raw("UPDATE xf_waf_config SET ? = ? WHERE appname = ?", key, value, appname).Exec()
	res, err := o.Raw(sqlstr).Exec()
	if err == nil {
		num, _ := res.RowsAffected()
		log.Printf("[INFO] UpdateWafConfigByAppname affected nums:%d\n", num)
		return num, nil
	}
	return 0, err
}

//生效配置该函数暂时未用到
func ApplyConfig(appname string) string {
	//获取应用的接入信息,包括规则是采用全部自定义，还是默认系统规则
	//调用接入IP重新读取waf_config,reqlimit_config,rule,black_ip_v2
	var xfapp *Xf_app
	o := orm.NewOrm()
	sqlstr := fmt.Sprintf("select * from xf_app where appname = '%s'", appname)
	err := o.Raw(sqlstr).QueryRow(&xfapp)
	if err != nil {
		log.Println(err)
	}
	if xfapp.Jrtype == "ecs" {
		var sNum int
		var failMsg string
		sIpList := strings.Split(xfapp.Serverip, ",")
		for _, sip := range sIpList {
			//方法一
			/*
				sipArry := strings.Split(sip, ":")
				if len(sipArry) > 0 {
					sip = sipArry[0]
				}
				durl := "http://" + sip + ":9527/vx/runcmd"
				bodystr := bytes.NewReader([]byte("export LANG=zh_cn.UTF-8;/usr/local/xwaf/xwaf.sh reload"))
				res, err := PostByProxy(durl, "192.168.238.83:9010", "POST", bodystr)
				if err != nil {
					log.Printf("[ERROR] reqfail %s,err:%s\n", sip, err)
					failMsg += fmt.Sprintf("%s", sip)
					continue
				}
			*/
			//方法二
			res, err := ReqUrl("http://"+sip+"/x/reload", "GET", "", "")
			if err != nil {
				log.Printf("[ERROR] reqfail %s,err:%s\n", sip, err)
				failMsg += fmt.Sprintf("%s", sip)
				continue
			}

			defer res.Body.Close()
			bodyBytes, _ := ioutil.ReadAll(res.Body)
			log.Printf("[INFO] ApplyConfig %s,result:%s\n", sip, string(bodyBytes))
			sNum++
		}
		if sNum == len(sIpList) {
			return "ok"
		} else {
			fMsg := fmt.Sprintf("all:%d,succes:%d,fail:%d,failIp:%s", len(sIpList), sNum, len(sIpList)-sNum, failMsg)
			return fMsg
		}
	}
	return "ok"
}

//创建应用时插入waf配置和reqlimit配置
func InsertConfig(appname string) (int64, error) {
	//INSERT INTO xf_waf_config(appname)VALUE('xxx');
	//INSERT INTO xf_reqlimit_config(appname)VALUE('xxx');
	o := orm.NewOrm()
	sqlstr := fmt.Sprintf("INSERT INTO xf_waf_config(appname)VALUE('%s')", appname)
	sqlstr2 := fmt.Sprintf("INSERT INTO xf_reqlimit_rule(appname,limit_url,limit_type,limit_key,limit_rate,limit_speed,limit_enable,limit_priority,remarks) SELECT '%s',limit_url,limit_type,limit_key,limit_rate,limit_speed,limit_enable,limit_priority,remarks FROM xf_reqlimit_rule WHERE appname = 'default'", appname)
	sqlstr3 := fmt.Sprintf("INSERT INTO xf_reqlimit_white_list(appname,ruletype,ruleitem,enable,remarks) SELECT '%s',ruletype,ruleitem,enable,remarks FROM xf_reqlimit_white_list WHERE appname = 'default'", appname)
	//主配置
	res, err := o.Raw(sqlstr).Exec()
	if err == nil {
		num, _ := res.RowsAffected()
		log.Println("[INFO] InsertConfig affected nums: ", num)
		//规则
		res2, err2 := o.Raw(sqlstr2).Exec()
		if err2 == nil {
			num2, _ := res2.RowsAffected()
			log.Println("[INFO] InsertConfig2 affected nums: ", num)
			//白名单
			res3, err3 := o.Raw(sqlstr3).Exec()
			if err3 == nil {
				num3, _ := res3.RowsAffected()
				log.Println("[INFO] InsertConfig3 affected nums: ", num3)
			}
			return num + num2, nil
		} else {
			return 0, err2
		}
	}
	return 0, err
}

//配置有变动时更新配置版本时间
func UpdateConfigTime(appname string) {
	if appname == "" {
		log.Printf("[ERROR] UpdateConfigTime fail,appname null\n")
		return
	}
	o := orm.NewOrm()
	dt := time.Now().Format("2006-01-02 15:04:05")
	var sqlstr string
	if appname == "sys" {
		sqlstr = fmt.Sprintf("UPDATE xf_waf_config SET updatetime = '%s'", dt)
	} else {
		sqlstr = fmt.Sprintf("UPDATE xf_waf_config SET updatetime = '%s' WHERE appname like '%s%%'", dt, appname)
	}
	res, err := o.Raw(sqlstr).Exec()
	if err == nil {
		num, _ := res.RowsAffected()
		log.Printf("[INFO] UpdateConfigTime affected nums:%d\n", num)
	} else {
		log.Printf("[ERROR] UpdateConfigTime fail:%s\n", err)
	}
}

//暂未用到
func HTMLMarshal(str string) (returnStr string) {
	bf := bytes.NewBuffer([]byte{})
	jsonEncoder := json.NewEncoder(bf)
	jsonEncoder.SetEscapeHTML(false)
	jsonEncoder.Encode(str)
	return bf.String()
}

/*
//更新limit配置页的自定义状态码和html
func UpdateLimitHtmlByAppname(appname, limitcode, limithtml string) (int64, error) {
	o := orm.NewOrm()
	//limithtml = HTMLMarshal(limithtml)
	limithtml = strings.Replace(limithtml, "\\", "\\\\", -1)
	limithtml = strings.Replace(limithtml, "%", "\\%", -1)
	limithtml = strings.Replace(limithtml, "'", "\\'", -1) //需要和第一行替换注意顺序
	sqlstr := fmt.Sprintf(`UPDATE xf_waf_config SET reqlimit_statuscode = '%s',limit_html = '%s' WHERE appname = '%s'`, limitcode, limithtml, appname)
	res, err := o.Raw(sqlstr).Exec()
	if err == nil {
		num, _ := res.RowsAffected()
		log.Printf("[INFO] UpdateLimitHtmlByAppname affected nums:%d\n", num)
		return num, nil
	}
	return 0, err
}

////更新waf配置页的输出模式和html
func UpdateWafModelByAppname(appname, wafmodel, wafhtml, wafRedirectUrl, wafExpireTime, urlLength string) (int64, error) {
	o := orm.NewOrm()
	//limithtml = HTMLMarshal(limithtml)
	wafhtml = strings.Replace(wafhtml, "\\", "\\\\", -1)
	wafhtml = strings.Replace(wafhtml, "%", "\\%", -1)
	wafhtml = strings.Replace(wafhtml, "'", "\\'", -1) //需要和第一行替换注意顺序
	sqlstr := fmt.Sprintf(`UPDATE xf_waf_config SET waf_model = '%s',waf_html = '%s',waf_redirect_url = '%s',waf_expire_time = '%s',waf_url_length = '%s' WHERE appname = '%s'`, wafmodel, wafhtml, wafRedirectUrl, wafExpireTime, urlLength, appname)
	res, err := o.Raw(sqlstr).Exec()
	if err == nil {
		num, _ := res.RowsAffected()
		log.Printf("[INFO] UpdateWafModelByAppname affected nums:%d\n", num)
		return num, nil
	}
	return 0, err
}
*/
func UpdateWafConfig(appname, logmodel, kfkip, waf_riskip_model, waf_riskip_ratev, limitcode, limithtml, wafmodel, wafhtml, wafRedirectUrl, wafExpireTime, urlLength, excludeCookie string) (int64, error) {
	o := orm.NewOrm()
	//limithtml = HTMLMarshal(limithtml)
	wafhtml = strings.Replace(wafhtml, "\\", "\\\\", -1)
	wafhtml = strings.Replace(wafhtml, "%", "\\%", -1)
	wafhtml = strings.Replace(wafhtml, "'", "\\'", -1) //需要和第一行替换注意顺序

	limithtml = strings.Replace(limithtml, "\\", "\\\\", -1)
	limithtml = strings.Replace(limithtml, "%", "\\%", -1)
	limithtml = strings.Replace(limithtml, "'", "\\'", -1) //需要和第一行替换注意顺序

	sqlstr := fmt.Sprintf(`UPDATE xf_waf_config SET log_model = '%s',kafka_broker_ip = '%s',waf_riskip_model = '%s', waf_riskip_ratev = '%s', reqlimit_statuscode = '%s',limit_html = '%s', waf_model = '%s',waf_html = '%s',waf_redirect_url = '%s',waf_expire_time = '%s',waf_url_length = '%s',exclude_cookie = '%s' WHERE appname = '%s'`, logmodel, kfkip, waf_riskip_model, waf_riskip_ratev, limitcode, limithtml, wafmodel, wafhtml, wafRedirectUrl, wafExpireTime, urlLength, excludeCookie, appname)
	res, err := o.Raw(sqlstr).Exec()
	if err == nil {
		num, _ := res.RowsAffected()
		UpdateConfigTime(appname)
		log.Printf("[INFO] UpdateWafConfigByAppname affected nums:%d\n", num)
		return num, nil
	}
	return 0, err
}
